package com.hzit.manager.service.impl;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphO;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.hzit.manager.handler.HzitBlockHandler;
import com.hzit.manager.handler.HzitFallbackHandler;
import com.hzit.manager.service.ISentinelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 类名：
 * 作者：WF
 * 功能：
 */
@Service
@Slf4j
public class SentinelServiceImpl implements ISentinelService {

	/**
	 * 1. 定义资源的第一种方案
	 */
	@Override
	public void res1() {
		// 1.5.0 版本开始可以利用 try-with-resources 特性（使用有限制）
		// 资源名可使用任意有业务语义的字符串，比如方法名、接口名或其它可唯一标识的字符串。
		Entry entry = null;
		// 务必保证 finally 会被执行
		try {
			entry = SphU.entry("res1");
			// 被保护的业务逻辑
			System.out.println("欢迎使用资源一");
		} catch (BlockException ex) {
			// 资源访问阻止，被限流或被降级
			log.error("资源一限流保护：" + ex);
		}finally {
			// 务必保证 exit，务必保证每个 entry 与 exit 配对
			if (entry != null) {
				entry.exit();
			}
		}
	}

	/**
	 * 2、 定义资源的第二种方案
	 */
	@Override
	public void res2() {
		// 资源名可使用任意有业务语义的字符串
		if (SphO.entry("res2")) {
			// 务必保证finally会被执行
			try {
				System.out.println("欢迎使用资源二");
			} finally {
				SphO.exit();
			}
		} else {
			// 资源访问阻止，被限流或被降级
			// 进行相应的处理操作
			log.error("资源二限流保护（限流保护处理）。" );
		}
	}

	/**
	 * 3. 定义并处理资源的第三种方案
	 */
	@Override
	/**
	 * 1. blockHandler 对应处理 BlockException 的函数名称，可选项。blockHandler 函数访问范围需要是 public，
	 * 返回类型需要与原方法相匹配，参数类型需要和原方法相匹配并且最后加一个额外的参数，类型为 BlockException。
	 * blockHandler 函数默认需要和原方法在同一个类中
	 * 2. 若希望使用其他类的函数，则可以指定 blockHandlerClass 为对应的类的 Class 对象，注意对应的函数必需为 static 函数，
	 * 否则无法解析
	 */
	// @SentinelResource(value="res3",blockHandler = "blockHandlerRes3")
	@SentinelResource(value = "res3",blockHandler = "blockHandlerRes3ByClass",blockHandlerClass = HzitBlockHandler.class)
	public void res3() {
		System.out.println("现在我们正在访问资源三！");
	}

	/**
	 * fallback / fallbackClass：fallback 函数名称，可选项，用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数
	 * 可以针对所有类型的异常（除了 exceptionsToIgnore 里面排除掉的异常类型）进行处理。fallback 函数签名和位置要求：
	 * 返回值类型必须与原函数返回值类型一致；
	 * 方法参数列表需要和原函数一致，或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
	 * fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数，则可以指定 fallbackClass 为对应的类的 Class 对象，
	 * 注意对应的函数必需为 static 函数，否则无法解析。
	 */
	// @SentinelResource(value = "res4",fallback = "fallbackHandler")
	@SentinelResource(value = "res4",fallback = "fallbackHandlerByClass",fallbackClass = HzitFallbackHandler.class)
	@Override
	public void res4() {
		int i = 10 / 0;
	}

	@SentinelResource(value = "message",blockHandler = "handlerMessageBlockHandler")
	@Override
	public void message() {
		System.out.println("res7资源");
	}
	public void handlerMessageBlockHandler(BlockException e){
		log.error("流控模式-链路限流异常！" + e );
	}

	public void fallbackHandler(Throwable e){
		log.error("出现了限流异常，使用fallback属性来处理异常," + e );
	}

	// 处理资源访问时的限流异常（在同一个类中写法）
	public void blockHandlerRes3(BlockException e){
		log.error("出现了限流异常，使用blockHandler属性来处理异常," + e );
	}
}
